/*
 *  modifier: Honrun
 *  date:     2023/10/29 20:42
 */
#include "stdio.h"
#include "string.h"
#include "microCPU.h"
#include "microMemory.h"


typedef struct{
    uint32_t startAddr;
    uint32_t stopAddr;
}MrMemoryType;


/* 动态内存头部信息 */
MrMemoryType g_typeMrMemoryHead = {0};
/* 需要增加 sizeof(MrMemoryType) * 2 个长度，以使字节对齐，以及存储管理信息 */
static uint8_t st_MrMemory[configMr_MEMORY_SIZE + sizeof(MrMemoryType) * 2] = {0};


void vMrMemoryInit(void)
{
    MrMemoryType *ptypeNow = NULL, *ptypeHanle = &g_typeMrMemoryHead;

    /* 地址 N 字节对齐 */
    ptypeHanle->startAddr = mrMemoryRoundUp((uint32_t)st_MrMemory, MR_MEMORY_ROUNDUP_VALUE);
    ptypeHanle->stopAddr  = ptypeHanle->startAddr + configMr_MEMORY_SIZE;

    /* 初始化第一个空闲空间 */
    ptypeNow  = (MrMemoryType *)ptypeHanle->startAddr;
    *ptypeNow = *ptypeHanle;

    /* 初始化内存空间末尾的管理信息，标志为已使用是为了在遍历时方便判断退出，以及方便free函数判断 */
    ptypeNow  = (MrMemoryType *)ptypeHanle->stopAddr;
    *ptypeNow = *ptypeHanle;
    /* 起始地址最低置1，表示已使用 */
    ptypeNow->startAddr |= 1;
}

void *pvMrMemoryMalloc(int32_t iSize)
{
    void *pvHandle = NULL;
    MrMemoryType *ptypeMin = NULL, *ptypeNow, *ptypeNext, *ptypeHanle = &g_typeMrMemoryHead;
    uint32_t uiAddrStop;
    int32_t iSizeNow, iSizeMin = 0x7FFFFFFF;

    if(iSize < 1)
        return NULL;

    /* 判断内存是否已经初始化 */
    if(g_typeMrMemoryHead.startAddr == g_typeMrMemoryHead.stopAddr)
        vMrMemoryInit();

    /* 长度 N 字节对齐 */
    iSize = mrMemoryRoundUp(iSize, MR_MEMORY_ROUNDUP_VALUE);

    /* 进入临界段 */
    vMrCpuDisableIsr();

    /* 遍历mem空间 */
    for(ptypeNow = (MrMemoryType *)ptypeHanle->startAddr; (uint32_t)ptypeNow < ptypeHanle->stopAddr; ptypeNow = (MrMemoryType *)ptypeNow->stopAddr)
    {
        if((ptypeNow->startAddr & 1) == 0)
        {
            iSizeNow = ptypeNow->stopAddr - ptypeNow->startAddr - sizeof(MrMemoryType);

            /* 最小空闲匹配 */
            if((iSize <= iSizeNow) && (iSizeNow < iSizeMin))
            {
                iSizeMin = iSizeNow;
                ptypeMin = ptypeNow;

                /* 找到刚好相同大小的空闲空间就直接退出遍历 */
                if(iSizeMin == iSize)
                    break;
            }
        }
    }

    if(ptypeMin != NULL)
    {
        /* 最小匹配空间被划分后还有剩余空间，并能够存储下一个头部信息 */
        if((iSizeMin - iSize) >= sizeof(MrMemoryType))
        {
            uiAddrStop = ptypeMin->stopAddr;
            ptypeMin->stopAddr = ptypeMin->startAddr + sizeof(MrMemoryType) + iSize;

            ptypeNext = (MrMemoryType *)ptypeMin->stopAddr;
            ptypeNext->startAddr = ptypeMin->stopAddr;
            ptypeNext->stopAddr  = uiAddrStop;
        }

        /* 返回空间管理信息之后的空间地址 */
        pvHandle = (void *)(ptypeMin->startAddr + sizeof(MrMemoryType));

        /* 起始地址最低置1，表示已使用 */
        ptypeMin->startAddr |= 1;
    }

    /* 退出临界段 */
    vMrCpuEnableIsr();

    return pvHandle;
}

void *pvMrMemoryCalloc(int32_t iNumber, int32_t iSize)
{
    void *pvHandle;

    iSize *= iNumber;

    /* 长度 N 字节对齐 */
    iSize = mrMemoryRoundUp(iSize, MR_MEMORY_ROUNDUP_VALUE);

    if((pvHandle = pvMrMemoryMalloc(iSize)) != NULL)
    {
        memset(pvHandle, 0, iSize);
    }

    return pvHandle;
}

void *pvMrMemoryRealloc(void *pvMem, int32_t iSize)
{
    void *pvHandle = NULL;
    MrMemoryType *ptypeOld, *ptypeNow, *ptypeNext, *ptypeHanle = &g_typeMrMemoryHead;
    uint32_t uiAddrStart;
    int32_t iNewSize;

    if((pvMem == NULL) || (iSize < 1))
        return NULL;

    /* 长度 N 字节对齐 */
    iSize = mrMemoryRoundUp(iSize, MR_MEMORY_ROUNDUP_VALUE);

    /* 进入临界段 */
    vMrCpuDisableIsr();

    /* 位移到头部管理信息位置 */
    uiAddrStart = (uint32_t)(pvMem - sizeof(MrMemoryType));
    if((uiAddrStart >= ptypeHanle->startAddr) && (uiAddrStart < ptypeHanle->stopAddr))
    {
        ptypeOld = (MrMemoryType *)(uiAddrStart);

        /* 计算后续衔接空闲空间的大小 */
        for(ptypeNow = (MrMemoryType *)ptypeOld->stopAddr; (ptypeNow->startAddr & 1) == 0; ptypeNow = (MrMemoryType *)(ptypeNow->stopAddr));

        uiAddrStart = ptypeOld->startAddr & 0xFFFFFFFE;
        iNewSize = (uint32_t)ptypeNow - uiAddrStart - sizeof(MrMemoryType);

        /* 直接拓展衔接的空闲空间 */
        if(iNewSize >= iSize)
        {
            /* 最小匹配空间被划分后还有剩余空间，并能够存储下一个头部信息 */
            if((iNewSize - iSize) >= sizeof(MrMemoryType))
            {
                ptypeOld->stopAddr = uiAddrStart + sizeof(MrMemoryType) + iSize;

                ptypeNext = (MrMemoryType *)ptypeOld->stopAddr;
                ptypeNext->startAddr = ptypeOld->stopAddr;
                ptypeNext->stopAddr  = (uint32_t)ptypeNow;
            }
            /* 扩容，不遗留用不上的小空闲空间 */
            else
            {
                ptypeOld->stopAddr = uiAddrStart + sizeof(MrMemoryType) + iNewSize;
            }

            pvHandle = pvMem;
        }
        /* 重新分配空间 */
        else if((pvHandle = pvMrMemoryMalloc(iSize)) != NULL)
        {
            /* 转移拷贝数据 */
            iSize = ptypeOld->stopAddr - uiAddrStart - sizeof(MrMemoryType);
            memcpy(pvHandle, pvMem, iSize);

            /* 释放旧空间 */
            vMrMemoryFree(pvMem);
        }
    }

    /* 退出临界段 */
    vMrCpuEnableIsr();

    return pvHandle;
}

void vMrMemoryFree(void *pvMem)
{
    MrMemoryType *ptypeNow, *ptypePrev, *ptypeNext, *ptypeHanle = &g_typeMrMemoryHead;
    uint32_t uiAddrStart;

    if(pvMem == NULL)
        return;

    /* 进入临界段 */
    vMrCpuDisableIsr();

    /* 位移到头部管理信息位置 */
    uiAddrStart = (uint32_t)(pvMem - sizeof(MrMemoryType));
    if((uiAddrStart >= ptypeHanle->startAddr) && (uiAddrStart < ptypeHanle->stopAddr))
    {
        ptypeNow = (MrMemoryType *)(uiAddrStart);

        if((ptypeNow->startAddr & 1) != 0)
        {
            /* 最低位清零，表示释放此片内存 */
            ptypeNow->startAddr &= 0xFFFFFFFE;

            /* 遍历，找到当前待删除空间的前面一个空间 */
            for(ptypePrev = (MrMemoryType *)ptypeHanle->startAddr; ptypePrev->stopAddr < ptypeNow->startAddr; ptypePrev = (MrMemoryType *)ptypePrev->stopAddr);

            /* 合并前面一个相连的空闲空间 */
            if((ptypePrev->startAddr & 1) == 0)
            {
                ptypePrev->stopAddr = ptypeNow->stopAddr;
                ptypeNow = ptypePrev;
            }

            /* 合并后面一个相连的空闲空间 */
            ptypeNext = (MrMemoryType *)ptypeNow->stopAddr;
            if((ptypeNext->startAddr & 1) == 0)
            {
                ptypeNow->stopAddr = ptypeNext->stopAddr;
            }
        }
    }

    /* 退出临界段 */
    vMrCpuEnableIsr();
}
